#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <time.h>
#include <stdlib.h>
#include <string>
#include <cstring>
#include <vector>
#include <sstream>
#include <exception>

#define    EQUAL    0
#define GT    1
#define    GTE    2
#define LT    3
#define LTE    4

class InvalidConditionException : public std::exception
{
    public:
    virtual const char* what() const throw()
    {
        return "Invalid condition received, returning\n";
    }
};

class InvalidOperatorException : public std::exception
{
    public:
    virtual const char* what() const throw()
    {
        return "Invalid operator received, returning\n";
    }
};

class InvalidStringFormatException : public std::exception
{
    public:
    virtual const char* what() const throw()
    {
        return "Text string not in \" \", returning\n";
    }
};

class GeneralConditionException : public std::exception
{
    public:
    virtual const char* what() const throw()
    {
        return "No condition to process, returning\n";
    }
};

//Definicja pojedynczego elementu krotki- jest to albo string albo integer- sprawdza się to przez flagę isInteger
struct Element 
{
    Element(std::string elementString)
    {
        eString = elementString;
        isInteger = false;
    }
    Element(int elementInteger)
    {
        eInteger = elementInteger;
        isInteger = true;
    }
    int operator==(const Element & other) const
    {
        return (eString == other.eString && eInteger == other.eInteger && isInteger == other.isInteger);
    }
    std::string eString;
    int eInteger;
    bool isInteger; //do sprawdzania, czy integer, czy string
};

//Definicja krotki

typedef std::vector<Element> Tuple;

// zbędne, elNum odpowiada Elements.size()
/*struct tuple
{
    std::vector<element> Elements;
    int elNum;    // ile elementów w krotce;
};*/

void linda_output(char * readBuffer, char * answerBuffer, std::vector<Tuple> &tuples);
void linda_input(char * readBuffer, char * answerBuffer, std::vector<Tuple> &tuples);
void linda_read(char * readBuffer, char * answerBuffer, std::vector<Tuple> &tuples);
void matchTuples(std::string condition, std::vector<Tuple> &tuples, unsigned index);
void matchTuplesSize(std::vector<Tuple> &tuples, unsigned size);
std::string convertTupleToString(Tuple tuple);
void removeTupleFromVector(std::vector<Tuple> &tuples, Tuple &tuple);
void linda_match(char * readBuffer, char * answerBuffer, std::vector<Tuple> &tuples);
bool checkIfStringComparision(std::string condition);
int getOperatorType(std::string condition, size_t operator_pos, int & operatorLength);
std::string getConditionValue(std::string condition, size_t operator_pos, int operatorLength, bool stringComparision);
void filterTuples(std::vector<Tuple> &tuples, std::string value, int operatorType, unsigned index, bool stringComparision);

int main(void)
{
    std::vector<Tuple> tuples;    //zestaw przetrzymywanych krotek
    char answerBuffer[512];
    char readBuffer[512] = "10.0.string:*,integer:>6,*:*,string:\"dupa\"";
    for(int i = 0; i < 10; i++)
    {
        Tuple t;
        t.push_back(Element("string"));
        t.push_back(Element(i));
        if(i % 2 == 0)
        {
            t.push_back(Element(152));
            t.push_back(Element("dupa"));
        }
        else
            t.push_back(Element("hej"));
        t.push_back(Element("dupa"));
        tuples.push_back(t);
    }

    printf("SIZE0: %d \n",tuples.size());
    for(int i = 0; i < tuples.size(); i++)
    {
        for(int j = 0; j < tuples[i].size(); j++)
        {
            if(tuples[i][j].isInteger)
                printf("%d,", tuples[i][j].eInteger);
            else
                printf("%s,", tuples[i][j].eString.c_str());
        }
        printf("\n");
    }

    printf("Request to linda: %s\n", readBuffer);
    
    linda_input(readBuffer, answerBuffer, tuples);

    for(int i = 0; i < tuples.size(); i++)
    {
        for(int j = 0; j < tuples[i].size(); j++)
        {
            if(tuples[i][j].isInteger)
                printf("%d,", tuples[i][j].eInteger);
            else
                printf("%s,", tuples[i][j].eString.c_str());
        }
        printf("\n");
    }

    printf("Answer from linda_input: %s\n", answerBuffer);

    linda_read(readBuffer, answerBuffer, tuples);
    printf("SIZE1: %d \n",tuples.size());
  for(int i = 0; i < tuples.size(); i++)
    {
        for(int j = 0; j < tuples[i].size(); j++)
        {
            if(tuples[i][j].isInteger)
                printf("%d,", tuples[i][j].eInteger);
            else
                printf("%s,", tuples[i][j].eString.c_str());
        }
        printf("\n");
    }
    char readBufferOut[512] = "11.2.3,18,\"string\",\"kawal_gada\",123,\"czupryna\",666";
    

    printf("Answer from linda_read: %s\n", answerBuffer);

    linda_output(readBufferOut, answerBuffer, tuples);
    char readBufferOut2[512] = "11.2.41,22,\"string\",312,\"kawal_gada\",123,\"ludy_wszelakie\"";
    linda_output(readBufferOut2, answerBuffer, tuples);

    printf("Answer from linda_output: %s\n", answerBuffer);

    printf("SIZE: %d \n",tuples.size());
    for(int i = 0; i < tuples.size(); i++)
    {
        for(int j = 0; j < tuples[i].size(); j++)
        {
            if(tuples[i][j].isInteger)
                printf("%d,", tuples[i][j].eInteger);
            else
                printf("%s,", tuples[i][j].eString.c_str());
        //printf("%d,", tuples[i][j].isInteger);
        }
        printf("\n");
    }

}

void linda_match(char * readBuffer, char * answerBuffer, std::vector<Tuple> &tuples, bool remove)
{
    std::string readBufferString(readBuffer);
    // we need to find childNumber that sent the request
    std::string childNumber = readBufferString.substr(0, readBufferString.find("."));
    //printf("Child number is: %s\n", childNumber.c_str());
    // now since we know the child number, we may focus solely on request content
    readBufferString = readBufferString.substr(readBufferString.find(".", readBufferString.find(".") + 1) + 1);
    std::string tupleString;

    std::vector<Tuple> helpTuples(tuples);
    bool stop = false;
    size_t pos = 0;
    unsigned index = 0;
    while(!stop)
    {
        pos = readBufferString.find(",");
        if(pos == std::string::npos)
            stop = true;
        std::string condition(readBufferString.substr(0, pos));
        readBufferString = readBufferString.substr(pos + 1);
        try
        {
            matchTuples(condition, helpTuples, index++);
        }
        catch(InvalidConditionException &e)
        {
            perror(e.what());
            std::string answerBufferString = childNumber + "." + "InvalidConditionException";
            strcpy(answerBuffer, answerBufferString.c_str());
            return;
        }
        catch(InvalidOperatorException &e)
        {
            perror(e.what());
            std::string answerBufferString = childNumber + "." + "InvalidOperatorException";
            strcpy(answerBuffer, answerBufferString.c_str());
            return;
        }
        catch(InvalidStringFormatException &e)
        {
            perror(e.what());
            std::string answerBufferString = childNumber + "." + "InvalidStringFormatException";
            strcpy(answerBuffer, answerBufferString.c_str());
            return;
        }
    }

    matchTuplesSize(helpTuples, index);

    if(helpTuples.size() != 0)
    {
        tupleString = convertTupleToString(helpTuples[0]);
    }

    std::string answerBufferString = childNumber + "." + tupleString;
    printf("Zwroce nastepujaca krotke: %s\n", answerBufferString.c_str());
    strcpy(answerBuffer, answerBufferString.c_str());

    if(remove)
        removeTupleFromVector(tuples, helpTuples[0]);

    /*printf("helpTuples:\n");
    for(int i = 0; i < helpTuples.size(); i++)
    {
        for(int j = 0; j < helpTuples[i].size(); j++)
        {
            if(helpTuples[i][j].isInteger)
                printf("%d,", helpTuples[i][j].eInteger);
            else
                printf("%s,", helpTuples[i][j].eString.c_str());
        }
        printf("\n");
    }
    printf("\n");*/
}

void linda_input(char * readBuffer, char * answerBuffer, std::vector<Tuple> &tuples)
{
    linda_match(readBuffer, answerBuffer, tuples, true);
}

void linda_read(char * readBuffer, char * answerBuffer, std::vector<Tuple> &tuples)
{
    linda_match(readBuffer, answerBuffer, tuples, false);
}

void matchTuples(std::string condition, std::vector<Tuple> &tuples, unsigned index)
{
    try
    {
        bool stringComparision = checkIfStringComparision(condition);
        size_t operator_pos;
        if(stringComparision)
            operator_pos = 6;
        else
            operator_pos = 7;
        int operatorLength = 1;
        int operatorType = getOperatorType(condition, operator_pos, operatorLength);
        std::string value = getConditionValue(condition, operator_pos, operatorLength, stringComparision);
        filterTuples(tuples, value, operatorType, index, stringComparision);
    }
    catch(GeneralConditionException &e)
    {
        // everything is ok, just return
        return;
    }
    catch(InvalidConditionException &e)
    {
        throw InvalidConditionException();
    }
    catch(InvalidOperatorException &e)
    {
        throw InvalidOperatorException();
    }
    catch(InvalidStringFormatException &e)
    {
        throw InvalidStringFormatException();
    }
}

void matchTuplesSize(std::vector<Tuple> &tuples, unsigned size)
{
    for(std::vector<Tuple>::iterator it = tuples.begin(); it != tuples.end(); ++it)
    {
        if(it->size() != size)
            tuples.erase(it--);
    }
}

std::string convertTupleToString(Tuple tuple)
{
    std::stringstream ss;
    for(unsigned i = 0; i < tuple.size(); i++)
    {
        if(tuple[i].isInteger)
        {
            ss << tuple[i].eInteger;
            ss << ",";
        }
        else
            ss << '"' + tuple[i].eString + "\",";
    }
    std::string response;
    ss >> response;
    return response.substr(0, response.size() - 1);
}

void removeTupleFromVector(std::vector<Tuple> &tuples, Tuple &tuple)
{
    for(std::vector<Tuple>::iterator it = tuples.begin(); it != tuples.end(); ++it)
        if(*it == tuple)
        {
            tuples.erase(it);
            return;
        }
}

bool checkIfStringComparision(std::string condition)
{
    if(condition == "*:*")
    {
        throw GeneralConditionException();
    }
    else if(condition.substr(0, 6) == "string")
    {
        return true;
    }
    else if(condition.substr(0, 7) == "integer")
    {
        return false;
    }
    else
    {
        throw InvalidConditionException();
    }
}

int getOperatorType(std::string condition, size_t operator_pos, int & operatorLength)
{
    if(condition.substr(operator_pos, 1) == ":")
    {
        if(condition.substr(operator_pos + 1, 1) == ">")
        {
            operatorLength = 2;
            return GTE;
        }
        else if(condition.substr(operator_pos + 1, 1) == "<")
        {
            operatorLength = 2;
            return LTE;
        }
        else 
            return EQUAL;
    }
    else if(condition.substr(operator_pos, 1) == ">")
        return GT;
    else if(condition.substr(operator_pos, 1) == "<")
        return LT;
    else
    {
        throw InvalidOperatorException();
    }
}

std::string getConditionValue(std::string condition, size_t operator_pos, int operatorLength, bool stringComparision)
{
    std::string value = condition.substr(operator_pos + operatorLength);
    if(stringComparision && value != "*")
    {
        if(value[0] != '"' || value[value.size()-1] != '"')
        {
            throw InvalidStringFormatException();
        }
        else
           return value.substr(1, value.size() - 2);
    }

    return value;
}

void filterTuples(std::vector<Tuple> &tuples, std::string value, int operatorType, unsigned index, bool stringComparision)
{
    for(std::vector<Tuple>::iterator it = tuples.begin(); it != tuples.end(); ++it)
    {
        if(stringComparision)
        {
            if((*it)[index].isInteger)
            {
                tuples.erase(it--);
            }
            else
            {
                if(value != "*")
                {
                    switch(operatorType)
                    {
                        case EQUAL : if((*it)[index].eString != value) tuples.erase(it--); break;
                        case GT : if((*it)[index].eString <= value) tuples.erase(it--); break;
                        case GTE : if((*it)[index].eString < value) tuples.erase(it--); break;
                        case LT : if((*it)[index].eString >= value) tuples.erase(it--); break;
                        case LTE : if((*it)[index].eString > value) tuples.erase(it--); break;
                    }
                }
            }
        }
        else
        {
            if(!(*it)[index].isInteger)
            {
                tuples.erase(it--);
            }
            else
            {
                if(value != "*")
                {
                    switch(operatorType)
                    {
                        case EQUAL : if((*it)[index].eInteger != atoi(value.c_str())) tuples.erase(it--); break;
                        case GT : if((*it)[index].eInteger <= atoi(value.c_str())) tuples.erase(it--); break;
                        case GTE : if((*it)[index].eInteger < atoi(value.c_str())) tuples.erase(it--); break;
                        case LT : if((*it)[index].eInteger >= atoi(value.c_str())) tuples.erase(it--); break;
                        case LTE : if((*it)[index].eInteger > atoi(value.c_str())) tuples.erase(it--); break;
                    }
                }
            }
        }
    }
}





void linda_output(char * readBuffer, char * answerBuffer, std::vector<Tuple> &tuples)
{

    

    std::string readBufferString(readBuffer);
    std::string childNumber = readBufferString.substr(0, readBufferString.find("."));
    readBufferString = readBufferString.substr(readBufferString.find(".", readBufferString.find(".") + 1) + 1);
   // printf("STRING is: %s\n", readBufferString.c_str());

    Tuple NewTuple;
    std::string HelpStr;    
    try
    {
    for(int i=0; i<= readBufferString.size(); i++)
    {
        if(readBufferString[i]==',')
        {
            
            if(HelpStr[0]=='"')
            {
                if(HelpStr[(HelpStr.size()-1)]!='"')throw InvalidStringFormatException();
                NewTuple.push_back(Element(HelpStr.substr(1,HelpStr.size()-2)));
            }
            else
            {
                if(HelpStr[(HelpStr.size()-1)]=='"')throw InvalidStringFormatException();
                NewTuple.push_back(Element(atoi(HelpStr.c_str())));
            }
            
            printf("SUBSTRING is: %s\n", HelpStr.c_str());
            HelpStr.clear();
        }
        else if(i==readBufferString.size()-1)
        {
            HelpStr+=readBufferString[i];
            if(HelpStr[0]=='"')
            {
                if(HelpStr[(HelpStr.size()-1)]!='"')throw InvalidStringFormatException();
                NewTuple.push_back(Element(HelpStr.substr(1,HelpStr.size()-2)));
            }
            else
            {
                if(HelpStr[(HelpStr.size()-1)]=='"')throw InvalidStringFormatException();
                NewTuple.push_back(Element(atoi(HelpStr.c_str())));
            }
            
            printf("SUBSTRING is: %s\n", HelpStr.c_str());
            HelpStr.clear();
        }
        else
        HelpStr+=readBufferString[i];
      }
   }
   catch(InvalidStringFormatException &e)
   {
     perror(e.what());
     std::string answerBufferString = childNumber + "." + "InvalidStringFormatException";
     strcpy(answerBuffer, answerBufferString.c_str());
     return;
   }

    tuples.push_back(NewTuple);

}